#pragma once

#include <math.h>
#include <stdlib.h>

#define Assert(_exp) ((void)0)

#define FastSqrt(x) (sqrt)(x)

#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h

#define M_PI_F ((float)(M_PI)) // Shouldn't collide with anything.

#define M_PHI 1.61803398874989484820 // golden ratio

// NJS: Inlined to prevent floats from being autopromoted to doubles, as with
// the old system.
#ifndef RAD2DEG
#define RAD2DEG(x) ((float)(x) * (float)(180.f / M_PI_F))
#endif

#ifndef DEG2RAD
#define DEG2RAD(x) ((float)(x) * (float)(M_PI_F / 180.f))
#endif

// MOVEMENT INFO
enum {
  PITCH = 0, // up / down
  YAW,       // left / right
  ROLL       // fall over
};

// decls for aligning data

#define DECL_ALIGN(x) __attribute__((aligned(x)))

#define ALIGN16 DECL_ALIGN(16)
#define VALVE_RAND_MAX 0x7fff
#define VectorExpand(v) (v).x, (v).y, (v).z

struct matrix3x4_t {
  matrix3x4_t() {}
  matrix3x4_t(float m00, float m01, float m02, float m03, float m10, float m11,
              float m12, float m13, float m20, float m21, float m22,
              float m23) {
    m_flMatVal[0][0] = m00;
    m_flMatVal[0][1] = m01;
    m_flMatVal[0][2] = m02;
    m_flMatVal[0][3] = m03;
    m_flMatVal[1][0] = m10;
    m_flMatVal[1][1] = m11;
    m_flMatVal[1][2] = m12;
    m_flMatVal[1][3] = m13;
    m_flMatVal[2][0] = m20;
    m_flMatVal[2][1] = m21;
    m_flMatVal[2][2] = m22;
    m_flMatVal[2][3] = m23;
  }

  float *operator[](int i) {
    Assert((i >= 0) && (i < 3));
    return m_flMatVal[i];
  }
  const float *operator[](int i) const {
    Assert((i >= 0) && (i < 3));
    return m_flMatVal[i];
  }
  float *Base() { return &m_flMatVal[0][0]; }
  const float *Base() const { return &m_flMatVal[0][0]; }

  float m_flMatVal[3][4];
};

class VMatrix {
public:
  VMatrix() {}
  VMatrix(float m00, float m01, float m02, float m03, float m10, float m11,
          float m12, float m13, float m20, float m21, float m22, float m23,
          float m30, float m31, float m32, float m33) {
    m[0][0] = m00;
    m[0][1] = m01;
    m[0][2] = m02;
    m[0][3] = m03;

    m[1][0] = m10;
    m[1][1] = m11;
    m[1][2] = m12;
    m[1][3] = m13;

    m[2][0] = m20;
    m[2][1] = m21;
    m[2][2] = m22;
    m[2][3] = m23;

    m[3][0] = m30;
    m[3][1] = m31;
    m[3][2] = m32;
    m[3][3] = m33;
  }

  // array access
  inline float *operator[](int i) { return m[i]; }

  inline const float *operator[](int i) const { return m[i]; }

  // Get a pointer to m[0][0]
  inline float *Base() { return &m[0][0]; }

  inline const float *Base() const { return &m[0][0]; }

public:
  // The matrix.
  float m[4][4];
};

class Vector {
public:
  float x, y, z;
  Vector(void);
  Vector(float X, float Y, float Z);
  void Init(float ix = 0.0f, float iy = 0.0f, float iz = 0.0f);
  bool IsValid() const;
  float operator[](int i) const;
  float &operator[](int i);
  inline void Zero();
  bool operator==(const Vector &v) const;
  bool operator!=(const Vector &v) const;
  inline Vector &operator+=(const Vector &v);
  inline Vector &operator-=(const Vector &v);
  inline Vector &operator*=(const Vector &v);
  inline Vector &operator*=(float s);
  inline Vector &operator/=(const Vector &v);
  inline Vector &operator/=(float s);
  inline Vector &operator+=(float fl);
  inline Vector &operator-=(float fl);
  inline float Length() const;
  inline float LengthSqr(void) const { return (x * x + y * y + z * z); }
  bool IsZero(float tolerance = 0.01f) const {
    return (x > -tolerance && x < tolerance && y > -tolerance &&
            y < tolerance && z > -tolerance && z < tolerance);
  }
  Vector Normalize();
  void NormalizeInPlace();
  inline float DistTo(const Vector &vOther) const;
  inline float DistToSqr(const Vector &vOther) const;
  float Dot(const Vector &vOther) const;
  float Length2D(void) const;
  float Length2DSqr(void) const;
  Vector &operator=(const Vector &vOther);
  Vector operator-(void) const;
  Vector operator+(const Vector &v) const;
  Vector operator-(const Vector &v) const;
  Vector operator*(const Vector &v) const;
  Vector operator/(const Vector &v) const;
  Vector operator*(float fl) const;
  Vector operator/(float fl) const;
  // Base address...
  float *Base();
  float const *Base() const;
};

//===============================================
inline void Vector::Init(float ix, float iy, float iz) {
  x = ix;
  y = iy;
  z = iz;
}
//===============================================
inline Vector::Vector(float X, float Y, float Z) {
  x = X;
  y = Y;
  z = Z;
}
//===============================================
inline Vector::Vector(void) { Zero(); }
//===============================================
inline void Vector::Zero() { x = y = z = 0.0f; }
//===============================================
inline void VectorClear(Vector &a) { a.x = a.y = a.z = 0.0f; }
//===============================================
inline Vector &Vector::operator=(const Vector &vOther) {
  x = vOther.x;
  y = vOther.y;
  z = vOther.z;
  return *this;
}
//===============================================
inline float &Vector::operator[](int i) {
  Assert((i >= 0) && (i < 3));
  return ((float *)this)[i];
}
//===============================================
inline float Vector::operator[](int i) const {
  Assert((i >= 0) && (i < 3));
  return ((float *)this)[i];
}
//===============================================
inline bool Vector::operator==(const Vector &src) const {
  return (src.x == x) && (src.y == y) && (src.z == z);
}
//===============================================
inline bool Vector::operator!=(const Vector &src) const {
  return (src.x != x) || (src.y != y) || (src.z != z);
}
//===============================================
inline void VectorCopy(const Vector &src, Vector &dst) {
  dst.x = src.x;
  dst.y = src.y;
  dst.z = src.z;
}
//===============================================
inline Vector &Vector::operator+=(const Vector &v) {
  x += v.x;
  y += v.y;
  z += v.z;
  return *this;
}
//===============================================
inline Vector &Vector::operator-=(const Vector &v) {
  x -= v.x;
  y -= v.y;
  z -= v.z;
  return *this;
}
//===============================================
inline Vector &Vector::operator*=(float fl) {
  x *= fl;
  y *= fl;
  z *= fl;

  return *this;
}
//===============================================
inline Vector &Vector::operator*=(const Vector &v) {
  x *= v.x;
  y *= v.y;
  z *= v.z;

  return *this;
}
//===============================================
inline Vector &Vector::operator+=(float fl) {
  x += fl;
  y += fl;
  z += fl;

  return *this;
}
//===============================================
inline Vector &Vector::operator-=(float fl) {
  x -= fl;
  y -= fl;
  z -= fl;

  return *this;
}
//===============================================
inline Vector &Vector::operator/=(float fl) {
  Assert(fl != 0.0f);
  float oofl = 1.0f / fl;
  x *= oofl;
  y *= oofl;
  z *= oofl;

  return *this;
}
//===============================================
inline Vector &Vector::operator/=(const Vector &v) {
  Assert(v.x != 0.0f && v.y != 0.0f && v.z != 0.0f);
  x /= v.x;
  y /= v.y;
  z /= v.z;

  return *this;
}
//===============================================
inline float Vector::Length(void) const {

  float root = 0.0f;

  float sqsr = x * x + y * y + z * z;

  root = sqrt(sqsr);

  return root;
}
//===============================================
inline float Vector::Length2D(void) const {
  float root = 0.0f;

  float sqst = x * x + y * y;

  root = sqrt(sqst);

  return root;
}
//===============================================
inline float Vector::Length2DSqr(void) const { return (x * x + y * y); }
//===============================================
inline Vector CrossProduct(const Vector &a, const Vector &b) {
  return Vector(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z,
                a.x * b.y - a.y * b.x);
}
//===============================================
float Vector::DistTo(const Vector &vOther) const {
  Vector delta;

  delta.x = x - vOther.x;
  delta.y = y - vOther.y;
  delta.z = z - vOther.z;

  return delta.Length();
}
float Vector::DistToSqr(const Vector &vOther) const {
  Vector delta;

  delta.x = x - vOther.x;
  delta.y = y - vOther.y;
  delta.z = z - vOther.z;

  return delta.LengthSqr();
}
//===============================================
inline Vector Vector::Normalize() {
  Vector vector;
  float length = this->Length();

  if (length != 0) {
    vector.x = x / length;
    vector.y = y / length;
    vector.z = z / length;
  } else {
    vector.x = vector.y = 0.0f;
    vector.z = 1.0f;
  }

  return vector;
}
//===============================================
inline void Vector::NormalizeInPlace() {
  Vector &v = *this;

  float iradius = 1.f / (this->Length() + 1.192092896e-07F); // FLT_EPSILON

  v.x *= iradius;
  v.y *= iradius;
  v.z *= iradius;
}
//===============================================
inline float VectorNormalize(Vector &v) {
  Assert(v.IsValid());
  float l = v.Length();
  if (l != 0.0f) {
    v /= l;
  } else {
    // FIXME:
    // Just copying the existing implemenation; shouldn't res.z == 0?
    v.x = v.y = 0.0f;
    v.z = 1.0f;
  }
  return l;
}
//===============================================
inline float VectorNormalize(float *v) {
  return VectorNormalize(*(reinterpret_cast<Vector *>(v)));
}
//===============================================
inline Vector Vector::operator+(const Vector &v) const {
  Vector res;
  res.x = x + v.x;
  res.y = y + v.y;
  res.z = z + v.z;
  return res;
}

//===============================================
inline Vector Vector::operator-(const Vector &v) const {
  Vector res;
  res.x = x - v.x;
  res.y = y - v.y;
  res.z = z - v.z;
  return res;
}
//===============================================
inline Vector Vector::operator*(float fl) const {
  Vector res;
  res.x = x * fl;
  res.y = y * fl;
  res.z = z * fl;
  return res;
}
//===============================================
inline Vector Vector::operator*(const Vector &v) const {
  Vector res;
  res.x = x * v.x;
  res.y = y * v.y;
  res.z = z * v.z;
  return res;
}
//===============================================
inline Vector Vector::operator/(float fl) const {
  Vector res;
  res.x = x / fl;
  res.y = y / fl;
  res.z = z / fl;
  return res;
}
//===============================================
inline Vector Vector::operator/(const Vector &v) const {
  Vector res;
  res.x = x / v.x;
  res.y = y / v.y;
  res.z = z / v.z;
  return res;
}
inline float Vector::Dot(const Vector &vOther) const {
  const Vector &a = *this;

  return (a.x * vOther.x + a.y * vOther.y + a.z * vOther.z);
}

//-----------------------------------------------------------------------------
// length
//-----------------------------------------------------------------------------

inline float VectorLength(const Vector &v) {

  return (float)FastSqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}

// VECTOR SUBTRAC
inline void VectorSubtract(const Vector &a, const Vector &b, Vector &c) {

  c.x = a.x - b.x;
  c.y = a.y - b.y;
  c.z = a.z - b.z;
}

// VECTORADD
inline void VectorAdd(const Vector &a, const Vector &b, Vector &c) {

  c.x = a.x + b.x;
  c.y = a.y + b.y;
  c.z = a.z + b.z;
}

//-----------------------------------------------------------------------------
// Base address...
//-----------------------------------------------------------------------------
inline float *Vector::Base() { return (float *)this; }

inline float const *Vector::Base() const { return (float const *)this; }

inline void VectorMAInline(const float *start, float scale,
                           const float *direction, float *dest) {
  dest[0] = start[0] + direction[0] * scale;
  dest[1] = start[1] + direction[1] * scale;
  dest[2] = start[2] + direction[2] * scale;
}

inline void VectorMAInline(const Vector &start, float scale,
                           const Vector &direction, Vector &dest) {
  dest.x = start.x + direction.x * scale;
  dest.y = start.y + direction.y * scale;
  dest.z = start.z + direction.z * scale;
}

inline void VectorMA(const Vector &start, float scale, const Vector &direction,
                     Vector &dest) {
  VectorMAInline(start, scale, direction, dest);
}

inline void VectorMA(const float *start, float scale, const float *direction,
                     float *dest) {
  VectorMAInline(start, scale, direction, dest);
}

inline unsigned long &FloatBits(float &f) {
  return *reinterpret_cast<unsigned long *>(&f);
}

inline bool IsFinite(float f) {
  return ((FloatBits(f) & 0x7F800000) != 0x7F800000);
}

//=========================================================
// 2D Vector2D
//=========================================================

class Vector2D {
public:
  // Members
  float x, y;

  // Construction/destruction
  Vector2D(void);
  Vector2D(float X, float Y);
  Vector2D(const float *pFloat);

  // Initialization
  void Init(float ix = 0.0f, float iy = 0.0f);

  // Got any nasty NAN's?
  bool IsValid() const;

  // array access...
  float operator[](int i) const;
  float &operator[](int i);

  // Base address...
  float *Base();
  float const *Base() const;

  // Initialization methods
  void Random(float minVal, float maxVal);

  // equality
  bool operator==(const Vector2D &v) const;
  bool operator!=(const Vector2D &v) const;

  // arithmetic operations
  Vector2D &operator+=(const Vector2D &v);
  Vector2D &operator-=(const Vector2D &v);
  Vector2D &operator*=(const Vector2D &v);
  Vector2D &operator*=(float s);
  Vector2D &operator/=(const Vector2D &v);
  Vector2D &operator/=(float s);

  // negate the Vector2D components
  void Negate();

  // Get the Vector2D's magnitude.
  float Length() const;

  // Get the Vector2D's magnitude squared.
  float LengthSqr(void) const;

  // return true if this vector is (0,0) within tolerance
  bool IsZero(float tolerance = 0.01f) const {
    return (x > -tolerance && x < tolerance && y > -tolerance && y < tolerance);
  }

  float Normalize();

  // Normalize in place and return the old length.
  float NormalizeInPlace();

  // Compare length.
  bool IsLengthGreaterThan(float val) const;
  bool IsLengthLessThan(float val) const;

  // Get the distance from this Vector2D to the other one.
  float DistTo(const Vector2D &vOther) const;

  // Get the distance from this Vector2D to the other one squared.
  float DistToSqr(const Vector2D &vOther) const;

  // Copy
  void CopyToArray(float *rgfl) const;

  // Multiply, add, and assign to this (ie: *this = a + b * scalar). This
  // is about 12% faster than the actual Vector2D equation (because it's done
  // per-component rather than per-Vector2D).
  void MulAdd(const Vector2D &a, const Vector2D &b, float scalar);

  // Dot product.
  float Dot(const Vector2D &vOther) const;

  // assignment
  Vector2D &operator=(const Vector2D &vOther);

#ifndef VECTOR_NO_SLOW_OPERATIONS
  // copy constructors
  Vector2D(const Vector2D &vOther);

  // arithmetic operations
  Vector2D operator-(void) const;

  Vector2D operator+(const Vector2D &v) const;
  Vector2D operator-(const Vector2D &v) const;
  Vector2D operator*(const Vector2D &v) const;
  Vector2D operator/(const Vector2D &v) const;
  Vector2D operator*(float fl) const;
  Vector2D operator/(float fl) const;

  // Cross product between two vectors.
  Vector2D Cross(const Vector2D &vOther) const;

  // Returns a Vector2D with the min or max in X, Y, and Z.
  Vector2D Min(const Vector2D &vOther) const;
  Vector2D Max(const Vector2D &vOther) const;

#else

private:
  // No copy constructors allowed if we're in optimal mode
  Vector2D(const Vector2D &vOther);
#endif
};

//-----------------------------------------------------------------------------

const Vector2D vec2_origin(0, 0);
// const Vector2D vec2_invalid(3.40282347E+38F, 3.40282347E+38F);

//-----------------------------------------------------------------------------
// Vector2D related operations
//-----------------------------------------------------------------------------

// Vector2D clear
void Vector2DClear(Vector2D &a);

// Copy
void Vector2DCopy(const Vector2D &src, Vector2D &dst);

// Vector2D arithmetic
void Vector2DAdd(const Vector2D &a, const Vector2D &b, Vector2D &result);
void Vector2DSubtract(const Vector2D &a, const Vector2D &b, Vector2D &result);
void Vector2DMultiply(const Vector2D &a, float b, Vector2D &result);
void Vector2DMultiply(const Vector2D &a, const Vector2D &b, Vector2D &result);
void Vector2DDivide(const Vector2D &a, float b, Vector2D &result);
void Vector2DDivide(const Vector2D &a, const Vector2D &b, Vector2D &result);
void Vector2DMA(const Vector2D &start, float s, const Vector2D &dir,
                Vector2D &result);

// Store the min or max of each of x, y, and z into the result.
void Vector2DMin(const Vector2D &a, const Vector2D &b, Vector2D &result);
void Vector2DMax(const Vector2D &a, const Vector2D &b, Vector2D &result);

#define Vector2DExpand(v) (v).x, (v).y

// Normalization
float Vector2DNormalize(Vector2D &v);

// Length
float Vector2DLength(const Vector2D &v);

// Dot Product
float DotProduct2D(const Vector2D &a, const Vector2D &b);

// Linearly interpolate between two vectors
void Vector2DLerp(const Vector2D &src1, const Vector2D &src2, float t,
                  Vector2D &dest);

//-----------------------------------------------------------------------------
//
// Inlined Vector2D methods
//
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// constructors
//-----------------------------------------------------------------------------

inline Vector2D::Vector2D(void) {
#ifdef _DEBUG
  // Initialize to NAN to catch errors
  // x = y = float_NAN;
#endif
}

inline Vector2D::Vector2D(float X, float Y) {
  x = X;
  y = Y;
  Assert(IsValid());
}

inline Vector2D::Vector2D(const float *pFloat) {
  Assert(pFloat);
  x = pFloat[0];
  y = pFloat[1];
  Assert(IsValid());
}

//-----------------------------------------------------------------------------
// copy constructor
//-----------------------------------------------------------------------------

inline Vector2D::Vector2D(const Vector2D &vOther) {
  Assert(vOther.IsValid());
  x = vOther.x;
  y = vOther.y;
}

//-----------------------------------------------------------------------------
// initialization
//-----------------------------------------------------------------------------

inline void Vector2D::Init(float ix, float iy) {
  x = ix;
  y = iy;
  Assert(IsValid());
}

inline void Vector2D::Random(float minVal, float maxVal) {
  x = minVal + ((float)rand() / VALVE_RAND_MAX) * (maxVal - minVal);
  y = minVal + ((float)rand() / VALVE_RAND_MAX) * (maxVal - minVal);
}

inline void Vector2DClear(Vector2D &a) { a.x = a.y = 0.0f; }

//-----------------------------------------------------------------------------
// assignment
//-----------------------------------------------------------------------------

inline Vector2D &Vector2D::operator=(const Vector2D &vOther) {
  Assert(vOther.IsValid());
  x = vOther.x;
  y = vOther.y;
  return *this;
}

//-----------------------------------------------------------------------------
// Array access
//-----------------------------------------------------------------------------

inline float &Vector2D::operator[](int i) {
  Assert((i >= 0) && (i < 2));
  return ((float *)this)[i];
}

inline float Vector2D::operator[](int i) const {
  Assert((i >= 0) && (i < 2));
  return ((float *)this)[i];
}

//-----------------------------------------------------------------------------
// Base address...
//-----------------------------------------------------------------------------

inline float *Vector2D::Base() { return (float *)this; }

inline float const *Vector2D::Base() const { return (float const *)this; }

//-----------------------------------------------------------------------------
// IsValid?
//-----------------------------------------------------------------------------

inline bool Vector2D::IsValid() const { return IsFinite(x) && IsFinite(y); }

//-----------------------------------------------------------------------------
// comparison
//-----------------------------------------------------------------------------

inline bool Vector2D::operator==(const Vector2D &src) const {
  Assert(src.IsValid() && IsValid());
  return (src.x == x) && (src.y == y);
}

inline bool Vector2D::operator!=(const Vector2D &src) const {
  Assert(src.IsValid() && IsValid());
  return (src.x != x) || (src.y != y);
}

//-----------------------------------------------------------------------------
// Copy
//-----------------------------------------------------------------------------

inline void Vector2DCopy(const Vector2D &src, Vector2D &dst) {
  Assert(src.IsValid());
  dst.x = src.x;
  dst.y = src.y;
}

inline void Vector2D::CopyToArray(float *rgfl) const {
  Assert(IsValid());
  Assert(rgfl);
  rgfl[0] = x;
  rgfl[1] = y;
}

//-----------------------------------------------------------------------------
// standard math operations
//-----------------------------------------------------------------------------

inline void Vector2D::Negate() {
  Assert(IsValid());
  x = -x;
  y = -y;
}

inline Vector2D &Vector2D::operator+=(const Vector2D &v) {
  Assert(IsValid() && v.IsValid());
  x += v.x;
  y += v.y;
  return *this;
}

inline Vector2D &Vector2D::operator-=(const Vector2D &v) {
  Assert(IsValid() && v.IsValid());
  x -= v.x;
  y -= v.y;
  return *this;
}

inline Vector2D &Vector2D::operator*=(float fl) {
  x *= fl;
  y *= fl;
  Assert(IsValid());
  return *this;
}

inline Vector2D &Vector2D::operator*=(const Vector2D &v) {
  x *= v.x;
  y *= v.y;
  Assert(IsValid());
  return *this;
}

inline Vector2D &Vector2D::operator/=(float fl) {
  Assert(fl != 0.0f);
  float oofl = 1.0f / fl;
  x *= oofl;
  y *= oofl;
  Assert(IsValid());
  return *this;
}

inline Vector2D &Vector2D::operator/=(const Vector2D &v) {
  Assert(v.x != 0.0f && v.y != 0.0f);
  x /= v.x;
  y /= v.y;
  Assert(IsValid());
  return *this;
}

inline void Vector2DAdd(const Vector2D &a, const Vector2D &b, Vector2D &c) {
  Assert(a.IsValid() && b.IsValid());
  c.x = a.x + b.x;
  c.y = a.y + b.y;
}

inline void Vector2DSubtract(const Vector2D &a, const Vector2D &b,
                             Vector2D &c) {
  Assert(a.IsValid() && b.IsValid());
  c.x = a.x - b.x;
  c.y = a.y - b.y;
}

inline void Vector2DMultiply(const Vector2D &a, float b, Vector2D &c) {
  Assert(a.IsValid() && IsFinite(b));
  c.x = a.x * b;
  c.y = a.y * b;
}

inline void Vector2DMultiply(const Vector2D &a, const Vector2D &b,
                             Vector2D &c) {
  Assert(a.IsValid() && b.IsValid());
  c.x = a.x * b.x;
  c.y = a.y * b.y;
}

inline void Vector2DDivide(const Vector2D &a, float b, Vector2D &c) {
  Assert(a.IsValid());
  Assert(b != 0.0f);
  float oob = 1.0f / b;
  c.x = a.x * oob;
  c.y = a.y * oob;
}

inline void Vector2DDivide(const Vector2D &a, const Vector2D &b, Vector2D &c) {
  Assert(a.IsValid());
  Assert((b.x != 0.0f) && (b.y != 0.0f));
  c.x = a.x / b.x;
  c.y = a.y / b.y;
}

inline void Vector2DMA(const Vector2D &start, float s, const Vector2D &dir,
                       Vector2D &result) {
  Assert(start.IsValid() && IsFinite(s) && dir.IsValid());
  result.x = start.x + s * dir.x;
  result.y = start.y + s * dir.y;
}

// FIXME: Remove
// For backwards compatability
inline void Vector2D::MulAdd(const Vector2D &a, const Vector2D &b,
                             float scalar) {
  x = a.x + b.x * scalar;
  y = a.y + b.y * scalar;
}

inline void Vector2DLerp(const Vector2D &src1, const Vector2D &src2, float t,
                         Vector2D &dest) {
  dest[0] = src1[0] + (src2[0] - src1[0]) * t;
  dest[1] = src1[1] + (src2[1] - src1[1]) * t;
}

//-----------------------------------------------------------------------------
// dot, cross
//-----------------------------------------------------------------------------
inline float DotProduct2D(const Vector2D &a, const Vector2D &b) {
  Assert(a.IsValid() && b.IsValid());
  return (a.x * b.x + a.y * b.y);
}

// for backwards compatability
inline float Vector2D::Dot(const Vector2D &vOther) const {
  return DotProduct2D(*this, vOther);
}

//-----------------------------------------------------------------------------
// length
//-----------------------------------------------------------------------------
inline float Vector2DLength(const Vector2D &v) {
  Assert(v.IsValid());
  return (float)FastSqrt(v.x * v.x + v.y * v.y);
}

inline float Vector2D::LengthSqr(void) const {
  Assert(IsValid());
  return (x * x + y * y);
}

inline float Vector2D::NormalizeInPlace() { return Vector2DNormalize(*this); }

inline bool Vector2D::IsLengthGreaterThan(float val) const {
  return LengthSqr() > val * val;
}

inline bool Vector2D::IsLengthLessThan(float val) const {
  return LengthSqr() < val * val;
}

inline float Vector2D::Length(void) const { return Vector2DLength(*this); }

inline void Vector2DMin(const Vector2D &a, const Vector2D &b,
                        Vector2D &result) {
  result.x = (a.x < b.x) ? a.x : b.x;
  result.y = (a.y < b.y) ? a.y : b.y;
}

inline void Vector2DMax(const Vector2D &a, const Vector2D &b,
                        Vector2D &result) {
  result.x = (a.x > b.x) ? a.x : b.x;
  result.y = (a.y > b.y) ? a.y : b.y;
}

//-----------------------------------------------------------------------------
// Normalization
//-----------------------------------------------------------------------------
inline float Vector2DNormalize(Vector2D &v) {
  Assert(v.IsValid());
  float l = v.Length();
  if (l != 0.0f) {
    v /= l;
  } else {
    v.x = v.y = 0.0f;
  }
  return l;
}

//-----------------------------------------------------------------------------
// Get the distance from this Vector2D to the other one
//-----------------------------------------------------------------------------
inline float Vector2D::DistTo(const Vector2D &vOther) const {
  Vector2D delta;
  Vector2DSubtract(*this, vOther, delta);
  return delta.Length();
}

inline float Vector2D::DistToSqr(const Vector2D &vOther) const {
  Vector2D delta;
  Vector2DSubtract(*this, vOther, delta);
  return delta.LengthSqr();
}

//-----------------------------------------------------------------------------
// Computes the closest point to vecTarget no farther than flMaxDist from
// vecStart
//-----------------------------------------------------------------------------
inline void ComputeClosestPoint2D(const Vector2D &vecStart, float flMaxDist,
                                  const Vector2D &vecTarget,
                                  Vector2D *pResult) {
  Vector2D vecDelta;
  Vector2DSubtract(vecTarget, vecStart, vecDelta);
  float flDistSqr = vecDelta.LengthSqr();
  if (flDistSqr <= flMaxDist * flMaxDist) {
    *pResult = vecTarget;
  } else {
    vecDelta /= FastSqrt(flDistSqr);
    Vector2DMA(vecStart, flMaxDist, vecDelta, *pResult);
  }
}

//-----------------------------------------------------------------------------
//
// Slow methods
//
//-----------------------------------------------------------------------------

#ifndef VECTOR_NO_SLOW_OPERATIONS
#endif
//-----------------------------------------------------------------------------
// Returns a Vector2D with the min or max in X, Y, and Z.
//-----------------------------------------------------------------------------

inline Vector2D Vector2D::Min(const Vector2D &vOther) const {
  return Vector2D(x < vOther.x ? x : vOther.x, y < vOther.y ? y : vOther.y);
}

inline Vector2D Vector2D::Max(const Vector2D &vOther) const {
  return Vector2D(x > vOther.x ? x : vOther.x, y > vOther.y ? y : vOther.y);
}

//-----------------------------------------------------------------------------
// arithmetic operations
//-----------------------------------------------------------------------------

inline Vector2D Vector2D::operator-(void) const { return Vector2D(-x, -y); }

inline Vector2D Vector2D::operator+(const Vector2D &v) const {
  Vector2D res;
  Vector2DAdd(*this, v, res);
  return res;
}

inline Vector2D Vector2D::operator-(const Vector2D &v) const {
  Vector2D res;
  Vector2DSubtract(*this, v, res);
  return res;
}

inline Vector2D Vector2D::operator*(float fl) const {
  Vector2D res;
  Vector2DMultiply(*this, fl, res);
  return res;
}

inline Vector2D Vector2D::operator*(const Vector2D &v) const {
  Vector2D res;
  Vector2DMultiply(*this, v, res);
  return res;
}

inline Vector2D Vector2D::operator/(float fl) const {
  Vector2D res;
  Vector2DDivide(*this, fl, res);
  return res;
}

inline Vector2D Vector2D::operator/(const Vector2D &v) const {
  Vector2D res;
  Vector2DDivide(*this, v, res);
  return res;
}

inline Vector2D operator*(float fl, const Vector2D &v) { return v * fl; }

class QAngleByValue;
class QAngle {
public:
  // Members
  float x, y, z;

  // Construction/destruction
  QAngle(void);
  QAngle(float X, float Y, float Z);
  //      QAngle(RadianEuler const &angles);      // evil auto type promotion!!!

  // Allow pass-by-value
  operator QAngleByValue &() { return *((QAngleByValue *)(this)); }
  operator const QAngleByValue &() const {
    return *((const QAngleByValue *)(this));
  }

  // Initialization
  void Init(float ix = 0.0f, float iy = 0.0f, float iz = 0.0f);
  void Random(float minVal, float maxVal);

  // Got any nasty NAN's?
  bool IsValid() const;
  void Invalidate();

  // array access...
  float operator[](int i) const;
  float &operator[](int i);

  // Base address...
  float *Base();
  float const *Base() const;

  // equality
  bool operator==(const QAngle &v) const;
  bool operator!=(const QAngle &v) const;

  bool IsZero(float tolerance = 0.01f) const {
    return (x > -tolerance && x < tolerance && y > -tolerance &&
            y < tolerance && z > -tolerance && z < tolerance);
  }

  // arithmetic operations
  QAngle &operator+=(const QAngle &v);
  QAngle &operator-=(const QAngle &v);
  QAngle &operator*=(float s);
  QAngle &operator/=(float s);

  // Get the vector's magnitude.
  float Length() const;
  float LengthSqr() const;

  // negate the QAngle components
  // void  Negate();

  // No assignment operators either...
  QAngle &operator=(const QAngle &src);

#ifndef VECTOR_NO_SLOW_OPERATIONS
  // copy constructors

  // arithmetic operations
  QAngle operator-(void) const;

  QAngle operator+(const QAngle &v) const;
  QAngle operator-(const QAngle &v) const;
  QAngle operator*(float fl) const;
  QAngle operator/(float fl) const;
#else

private:
  // No copy constructors allowed if we're in optimal mode
  QAngle(const QAngle &vOther);

#endif
};

//-----------------------------------------------------------------------------
// constructors
//-----------------------------------------------------------------------------
inline QAngle::QAngle(void) {
#ifdef _DEBUG
#ifdef VECTOR_PARANOIA
  // Initialize to NAN to catch errors
  x = y = z = VEC_T_NAN;
#endif
#endif
}

inline QAngle::QAngle(float X, float Y, float Z) {
  x = X;
  y = Y;
  z = Z;
}

//-----------------------------------------------------------------------------
// initialization
//-----------------------------------------------------------------------------
inline void QAngle::Init(float ix, float iy, float iz) {
  x = ix;
  y = iy;
  z = iz;
}

inline void QAngle::Random(float minVal, float maxVal) {
  x = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal);
  y = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal);
  z = minVal + ((float)rand() / RAND_MAX) * (maxVal - minVal);
}

//-----------------------------------------------------------------------------
// assignment
//-----------------------------------------------------------------------------
inline QAngle &QAngle::operator=(const QAngle &vOther) {

  x = vOther.x;
  y = vOther.y;
  z = vOther.z;
  return *this;
}

//-----------------------------------------------------------------------------
// comparison
//-----------------------------------------------------------------------------
inline bool QAngle::operator==(const QAngle &src) const {

  return (src.x == x) && (src.y == y) && (src.z == z);
}

inline bool QAngle::operator!=(const QAngle &src) const {

  return (src.x != x) || (src.y != y) || (src.z != z);
}

//-----------------------------------------------------------------------------
// standard math operations
//-----------------------------------------------------------------------------
inline QAngle &QAngle::operator+=(const QAngle &v) {

  x += v.x;
  y += v.y;
  z += v.z;
  return *this;
}

inline QAngle &QAngle::operator-=(const QAngle &v) {

  x -= v.x;
  y -= v.y;
  z -= v.z;
  return *this;
}

inline QAngle &QAngle::operator*=(float fl) {
  x *= fl;
  y *= fl;
  z *= fl;

  return *this;
}

inline QAngle &QAngle::operator/=(float fl) {
  Assert(fl != 0.0f);
  float oofl = 1.0f / fl;
  x *= oofl;
  y *= oofl;
  z *= oofl;

  return *this;
}

//-----------------------------------------------------------------------------
// Base address...
//-----------------------------------------------------------------------------
inline float *QAngle::Base() { return (float *)this; }

inline float const *QAngle::Base() const { return (float const *)this; }

//-----------------------------------------------------------------------------
// Array access
//-----------------------------------------------------------------------------
inline float &QAngle::operator[](int i) {
  Assert((i >= 0) && (i < 3));
  return ((float *)this)[i];
}

inline float QAngle::operator[](int i) const {
  Assert((i >= 0) && (i < 3));
  return ((float *)this)[i];
}

//-----------------------------------------------------------------------------
// length
//-----------------------------------------------------------------------------
inline float QAngle::Length() const { return (float)FastSqrt(LengthSqr()); }

inline float QAngle::LengthSqr() const { return x * x + y * y + z * z; }

//-----------------------------------------------------------------------------
// arithmetic operations (SLOW!!)
//-----------------------------------------------------------------------------
#ifndef VECTOR_NO_SLOW_OPERATIONS

inline QAngle QAngle::operator-(void) const { return QAngle(-x, -y, -z); }

inline QAngle QAngle::operator+(const QAngle &v) const {
  QAngle res;
  res.x = x + v.x;
  res.y = y + v.y;
  res.z = z + v.z;
  return res;
}

inline QAngle QAngle::operator-(const QAngle &v) const {
  QAngle res;
  res.x = x - v.x;
  res.y = y - v.y;
  res.z = z - v.z;
  return res;
}

inline QAngle QAngle::operator*(float fl) const {
  QAngle res;
  res.x = x * fl;
  res.y = y * fl;
  res.z = z * fl;
  return res;
}

inline QAngle QAngle::operator/(float fl) const {
  QAngle res;
  res.x = x / fl;
  res.y = y / fl;
  res.z = z / fl;
  return res;
}

inline QAngle operator*(float fl, const QAngle &v) { return v * fl; }

#endif // VECTOR_NO_SLOW_OPERATIONS

// QANGLE SUBTRAC
inline void QAngleSubtract(const QAngle &a, const QAngle &b, QAngle &c) {

  c.x = a.x - b.x;
  c.y = a.y - b.y;
  c.z = a.z - b.z;
}

// QANGLEADD
inline void QAngleAdd(const QAngle &a, const QAngle &b, QAngle &c) {

  c.x = a.x + b.x;
  c.y = a.y + b.y;
  c.z = a.z + b.z;
}